#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@IDE     ：PyCharm 
@Author  ：kiway
@Date    ：2024/5/29 23:35 
"""
import asyncio
import multiprocessing
import traceback
from collections import defaultdict
from multiprocessing import Queue as mQueue
from .BaseEngine import BaseEngine, Event, EVENT_SWITCH
from typing import Callable
from ..logger import pretty_print
from .RowAsyncQ import Publisher
from .DataPersist import MarketSave
from .event import EVENT_BAR, EVENT_AGGTRADE

# Defines handler function to be used in event engine.
mHandlerType: callable = Callable[[Event], None]
import time


class EventEngine:
    """
    Event engine distributes event object based on its type
    to those handlers registered.

    It also generates timer event by every interval seconds,
    which can be used for timing purpose.
    """

    def __init__(self) -> None:
        """
        Timer event is generated every 1 second by default, if
        interval not specified.
        """
        self.__msg_q = mQueue()
        self.__msg_market_data_q = mQueue()
        self._handlers: defaultdict = defaultdict(list)
        self.market_save = MarketSave()

    def put_event(self, event):
        self.__msg_q.put(event)

    def put_market_data_event(self, data):
        self.__msg_market_data_q.put(data)

    def _to_save_market_data(self):

        while True:
            try:
                event = self.__msg_market_data_q.get()
                if event.type == EVENT_BAR:

                    self.market_save.save_bar(event.symbol, event.data)

                elif event.type == EVENT_AGGTRADE:
                    self.market_save.save_aggTrade(event.symbol, event.data)


            except:
                traceback.print_exc()

    def start(self):
        multiprocessing.Process(target=self._run).start()
        multiprocessing.Process(target=self._to_save_market_data).start()

    def _run(self):

        zmq_msg = Publisher()
        tm = int(time.time() * 1000)
        while True:
            event = self.__msg_q.get()
            t = int(time.time() * 1000)
            try:
                if event.type == EVENT_SWITCH:
                    if t - tm < 300:
                        continue
                    zmq_msg.put(EVENT_SWITCH)
                    tm = t
                else:
                    self._process(event)

            except:
                traceback.print_exc()

    def _process(self, event):
        if event.type in self._handlers:
            for handler in self._handlers[event.type]:
                handler(event)

    def register(self, type_: str, handler: mHandlerType) -> None:
        """
        Register a new handler function for a specific event type. Every
        function can only be registered once for each event type.
        """
        handler_list: list = self._handlers[type_]
        if handler not in handler_list:
            handler_list.append(handler)

    def unregister(self, type_: str, handler: mHandlerType) -> None:
        """
        Unregister an existing handler function from event engine.
        """
        handler_list: list = self._handlers[type_]

        if handler in handler_list:
            handler_list.remove(handler)

        if not handler_list:
            self._handlers.pop(type)
